#include <stdlib.h>
#define POISON 13666

/*!
 *  \struct stack_t
 *  \brief LIFO type of data
 *  \param double* Array - Array of data you'll keep in stack
 *  \param int Position - pointer, where the next item will be saved
 *  \warning int Position is a pointer to an unused place of stack! No useful data here
 *  \param int ElementsCount - maximum number of elements in stack. Usually can be wided, but not always
 */

struct stack_t
{
    double* Array;
    int Position;
    int ElementsCount;
};

/*!
 *  \enum StackError
 *  \brief Contains possible codes of errors in stackerrno \see stackerrno
 *  \param meaning of params are explained by char* stackerror(int code)
 *  \see stackerror
 */

enum StackError {NOERROR, STACKPOINTERNULL, MALLOCERROR, STACKARRAYNULLPOINTER,
                 BROKENPOSITION, NOTENOUGHMEMORY, NOELEMENTS};

/*!
 *  \var extern variable stackerrno
 *  \brief extern variable, where the code of the last error with stack is contained
 *  \see stackerror
 *  \see StackError
 */

enum StackError stackerrno = NOERROR;

/*!
 *  \fn Stack_ctor
 *  \brief Constructs a stack
 *  \param Stack is a pointer to a stack_t variable
 *  \param int ElementsAmount is a size of stack you want to create
 *  \return 0, if stack wasn't constructed and 1 in another case
 */

int Stack_ctor (struct stack_t* Stack, int ElementsAmount)
{
    if (!Stack)
    {
        stackerrno = STACKPOINTERNULL;
        return 0;
    }
    Stack -> ElementsCount = ElementsAmount;
    Stack -> Array = (double*) malloc(Stack -> ElementsCount*sizeof(double));  //Stack -> Array initialized later,
                                                                               //cause want to put POISON in unused
                                                                               //elements of array for more protection

    if (Stack -> Array == NULL)
    {
        stackerrno = MALLOCERROR;
        return 0;
    }
    for (int i = 0; i < Stack -> ElementsCount; i++)                           //here it's initialized
    {
        Stack -> Array[i] = POISON;
    }

    Stack -> Position = 0;
    return 1;
}

/*!
 *  \fn Stack_Ok
 *  \brief Analises the state of the stack and says, wheather it's broken or not
 *  \param Stack is a pointer to a stack_t variable
 *  \return 0 if Stack is broken and 1 in other case
 *  \note If stack is broken, writes the code of error in stackerrno \see stackerrno
 */

int Stack_Ok(struct stack_t* Stack)
{
    if (!Stack)
    {
        stackerrno = STACKPOINTERNULL;
        return 0;
    }
    if (Stack -> Array == NULL)
    {
        stackerrno = STACKARRAYNULLPOINTER;
        return 0;
    }
    if (Stack -> Position < 0 || Stack -> Position > Stack -> ElementsCount)
    {
        stackerrno = BROKENPOSITION;
        return 0;
    }
    for (int i = Stack -> Position; i < Stack -> ElementsCount; i++)
    {
        if (Stack -> Array[i] != POISON)
        {
            stackerrno = BROKENPOSITION;
            return 0;
            break;
        }
    }
    return 1;
}

/*!
 *  \fn stackerror
 *  \brief Tells the description of last error happend to stack, if its argument is stackerrno
 *  \param int Code - code of the error happened to stack
 *  \return String with explanation of error
 *  \note Use this function with extern variable stackerrno
 *  \see stackerrno
 *  \see StackError
 */

char* stackerror(int code)
{
    if (code == NOERROR)
    {
        return "There's no errors with stack";
    }
    if (code == STACKPOINTERNULL)
    {
        return "Stack Pointer is invalid";
    }
    if (code == MALLOCERROR)
    {
        return "Malloc didn't work properly";
    }
    if (code == STACKARRAYNULLPOINTER)
    {
        return "Array in stack has invalid pointer";
    }
    if (code == BROKENPOSITION)
    {
        return "Something's bad with stack counter";
    }
    if (code == NOTENOUGHMEMORY)
    {
        return "There's not enough memory for your stack";
    }
    if (code == NOELEMENTS)
    {
        return "There's no elements in your stack";
    }
}

/*!
 *  \fn Stack_dtor
 *  \brief Destructs the Stack
 *  \param Stack is a pointer to a stack_t variable
 *  \return 0 if the stack wasn't destroyed and 1 in other case
 *  \note if stack wasn't destroyed, writes in stackerrno code of error \see stackerrno
 */

int Stack_dtor (struct stack_t* Stack)
{
    if (Stack_Ok(Stack))
    {
        free(Stack -> Array);
        Stack -> Array = NULL;
        Stack -> Position = 0;
        Stack -> ElementsCount = 0;
        Stack = NULL;
        return 1;
    }
    return 0;
}

/*!
 *  \fn Stack_push
 *  \brief pushes item in the stack
 *  \param Stack is a pointer to a stack_t variable
 *  \param double Element is item you want to push in stack
 *  \return 0 if the item wasn't pushed, 1 in other case
 *  \note if item wasn't pushed writes code of error in stackerrno \see stackerrno
 */

int Stack_push(struct stack_t* Stack, double Element)
{
    if (Stack_Ok(Stack))
    {
        if (Stack -> Position < Stack -> ElementsCount)
        {
            Stack -> Array[Stack -> Position] = Element;
            Stack -> Position ++;
            return 1;
        }
        else
        {
            double* NewArray = (double*) realloc(Stack -> Array, (Stack -> ElementsCount + 1)*sizeof(double));
            if (!NewArray)
            {
                stackerrno = NOTENOUGHMEMORY;
                return 0;
            }
            else
            {
                Stack -> Array = NewArray;
                Stack -> ElementsCount++;
                Stack -> Position++;
                return 1;
            }
        }
    }
    return 0;
}

/*!
 *  \fn Stack_pull
 *  \brief pulls item from the stack
 *  \param Stack is a pointer to a stack_t variable
 *  \param double* Element is a pointer to variable, where pulled value will be placed
 *  \return 0 if the item wasn't pulled, 1 in other case
 *  \note if item wasn't pulled writes code of error in stackerrno \see stackerrno
 */

int Stack_pull(struct stack_t* Stack, double* Element)
{
    if(Stack_Ok(Stack))
    {
        if (Stack -> Position == 0)
        {
            stackerrno = NOELEMENTS;
            return 0;
        }
        else
        {
            *Element = Stack -> Array[Stack -> Position - 1];
            Stack -> Array[Stack -> Position - 1] = POISON;
            Stack -> Position--;
            return 1;
        }
    }
    else return 0;
}

/*!
 *  \fn Stack_dump
 *  \brief prints much info about stack
 *  \param Stack is a pointer to a stack_t variable
 *  \return 0 if Stack if ok, value if sackerrno in other case \see stackerrno
 */

int Stack_dump(struct stack_t* Stack)
{
    if (int a = Stack_Ok(Stack)) printf("%s \n", "Stack is ok");
        else printf ("%s\n%s\n" , "Stack is not ok!", stackerror(stackerrno));
    printf("%s %d\n%s %d\n", "Size of Stack:", Stack -> ElementsCount, "Number of elements in stack:", Stack -> Position);
    printf("%s \n", "Elements in stack:");
    for (int i = 0; i < Stack -> ElementsCount; i++)
    {
        printf("%d:%lg \n", i, Stack -> Array[i]);
    }
    if (a == 0) return stackerrno;
    else return 0;
}
